#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
#include <linux/timex.h>

#ifndef MAX_PATH
#define MAX_PATH 256
#endif

#define PACKAGE_SIZE 512

extern void msleep(unsigned int msecs);

struct proc_dir_entry * slam_dir = NULL;
struct proc_dir_entry * slam_entry = NULL;

static char * dir_name = "slam_song";
static char * entry_name = "path";

static void beep(unsigned int freq, unsigned int delay)
{

	unsigned int count;
	if (freq)
		count = PIT_TICK_RATE / freq;
	else
		count = 20;

	outb_p(0xB6, 0x43);
	outb_p(count & 0xff, 0x42);
	outb((count >> 8) & 0xff, 0x42);
	outb_p(inb_p(0x61) | 0x03, 0x61);
	msleep(delay);
	outb(inb_p(0x61) & 0xFC, 0x61);
}

static int play(char * filename)
{
	struct file * fp;
	mm_segment_t cur_mm_seg;
	loff_t fpos = 0;

	char buf;
	int data[PACKAGE_SIZE] = {0};
	int i = 0, j = 0; //i:data count,j:just temp value

	fp = filp_open(filename, O_RDONLY, 0644);
	if (IS_ERR(fp)) {
		printk("<xinu>filp_open error\n");
		return -1;
	}

	cur_mm_seg = get_fs();
	set_fs(KERNEL_DS);

	while(vfs_read(fp, &buf, sizeof(buf), &fpos)>0)
	{
		if(buf == ',')
		{
			i++;
			continue;
		}

		if(buf == '\n' || buf == '\r')
			continue;

		data[i] = data[i] * 10 + (buf - '0');
	}

	set_fs(cur_mm_seg);

	filp_close(fp, NULL);

	for (j = 0; j < i; j += 2) {
		beep(data[j], data[j+1]);
	}

	return 0;
}

static ssize_t song_path_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
	char filename[MAX_PATH];

	if(count > MAX_PATH)
	{
		printk("The filename is too long,the length must < %d\n", MAX_PATH);
		return -EFAULT;
	}

	if(copy_from_user(filename, buf, count))
	{
		printk("copy_from_user error!\n");
		return -EFAULT;
	}

	filename[count-1] = '\0';

	play(filename);

	return count;
}

static const struct file_operations slam_fops = 
{
	.owner = THIS_MODULE,
	.write = song_path_write,
};

static int beep_songs_init(void)
{
	#ifdef CONFIG_PROC_FS
		slam_dir = proc_mkdir(dir_name, NULL);
		if (!slam_dir)
		{
			printk("Create directory \"%s\" failed.\n", dir_name);
			return -1;
		}

		slam_entry = proc_create(entry_name, 0666, slam_dir, &slam_fops);
		if (!slam_entry)
		{
			printk("Create file \"%s\" failed.\n", entry_name);
			return -1;
		}
	#else
		printk("This module requests PROCFS support in linux kernel,need set CONFIG_PROC_FS configure Y\n");
	#endif

	return 0;
}

static void beep_songs_exit(void)
{
	#ifdef CONFIG_PROC_FS
		proc_remove(slam_entry);
		proc_remove(slam_dir);
	#endif

	printk("Bye %s!\n", __func__);
}

module_init(beep_songs_init);
module_exit(beep_songs_exit);

MODULE_LICENSE("GPL");
